package org.test4j.mock.faking.util;

import java.util.ArrayList;
import java.util.List;

import static java.lang.Thread.currentThread;

class SearchingClassLoader extends ClassLoader {
    public static final ClassLoader THIS_CL = ClassLoad.class.getClassLoader();

    private final ClassLoader nextToSearch;

    private SearchingClassLoader(ClassLoader parent, ClassLoader nextToSearch) {
        super(parent);
        this.nextToSearch = nextToSearch;
    }

    private static ClassLoader combine(List<ClassLoader> parents) {
        ClassLoader loader = parents.get(parents.size() - 1);
        for (int i = parents.size() - 2; i >= 0; i--) {
            loader = new SearchingClassLoader(parents.get(i), loader);
        }
        return loader;
    }

    static ClassLoader combineLoadersOf(Class first, Class... types) {
        List<ClassLoader> loaders = new ArrayList<ClassLoader>();
        addIfNewElement(loaders, first == null ? null : first.getClassLoader());
        for (Class type : types) {
            addIfNewElement(loaders, type == null ? null : type.getClassLoader());
        }
        addIfNewElement(loaders, THIS_CL);
        addIfNewElement(loaders, currentThread().getContextClassLoader());
        addIfNewElement(loaders, ClassLoader.getSystemClassLoader());
        return combine(loaders);
    }

    private static void addIfNewElement(List<ClassLoader> loaders, ClassLoader c) {
        if (c != null && !loaders.contains(c)) {
            loaders.add(c);
        }
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        if (nextToSearch != null) {
            return nextToSearch.loadClass(name);
        } else {
            /**
             * will throw ClassNotFoundException
             */
            return super.findClass(name);
        }
    }
}